﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Common.Utilities.OpenXml;

namespace Workflows.Components.TextMining.FullTextSearch
{
    /// <summary>
    /// 
    /// </summary>
    public partial class SearchFieldContributionEditor : Form, IComponentEditor 
    {
        private SearchFieldContributionComponent _FieldContributor;
        private object _ActivityNode;

        /// <summary>
        /// 
        /// </summary>
        public SearchFieldContributionEditor()
        {
            InitializeComponent();
        }

        private void SearchFieldContributionEditor_Load(object sender, EventArgs e)
        {
            this.cbo_FilterMethod.Items.Clear();
            for(int i=0;i<=3;i++)
            {
                FullTextSearchFilterMethod filterMethod = (FullTextSearchFilterMethod) i;
                this.cbo_FilterMethod.Items.Add(filterMethod.ToString());
            }
        }

        #region Implementation of IComponentEditor

        public event UpdateComponentSettings UpdateComponentSettingEvent;

        /// <summary></summary>
        public object OwnerActivityNode
        {
            get { return _ActivityNode; }
            set { _ActivityNode=value; }
        }

        /// <summary></summary>
        public Type WorkflowComponentType
        {
            get { return typeof (SearchFieldContributionComponent); }
        }

        /// <summary></summary>
        public void LoadSettings(GlobalVariable[] globalVars, WorkflowComponentBase[] fromComponents, object activityNode, WorkflowComponentBase wfComponent)
        {
            this._ActivityNode = activityNode;
            this._FieldContributor = (SearchFieldContributionComponent) wfComponent;
            SearchFieldContributionInstruction instruction =
                (SearchFieldContributionInstruction) this._FieldContributor.Instruction;
            this.txt_ConnStr.Text = instruction.ConnStr;
            this.PopulateTables(instruction.ConnStr);
            this.SelectComboBoxItem(this.cbo_Tables,instruction.TableName);
            this.SelectComboBoxItem(this.cbo_KeyField,instruction.KeyField);
            this.SelectComboBoxItem(this.cbo_TitleField, instruction.TitleField);
            this.dv_TextFields.Rows.Clear();
            if(instruction.TextFields !=null && instruction.TextFields.Count>0)
            {
                foreach(string txtField in instruction.TextFields)
                {
                    DataGridViewRow dr = new DataGridViewRow();
                    DataGridViewTextBoxCell cellFieldName=new DataGridViewTextBoxCell();
                    cellFieldName.Value = txtField;
                    dr.Cells.Add(cellFieldName);
                    this.dv_TextFields.Rows.Add(dr);
                }
            }
            this.txt_Query.Text = instruction.Query;
            this.SelectComboBoxItem(this.cbo_FilterMethod, instruction.FilterMethod.ToString());
            this.num_FilterThreshold.Value =(decimal) instruction.FilterThreshold;
            this.txt_DataSourcePath.Text = instruction.StandardSetDataSourcePath;
            switch(instruction.StandardDataSourceType)
            {
                case SetDataSourceType.DB:
                    this.rd_Db.Checked = true;
                    break;
                case SetDataSourceType.ExcelFile:
                    this.rd_Excel.Checked = true;
                    break;
                case SetDataSourceType.XML:
                    this.rd_Xml.Checked = true;
                    break;
            }
            this.cbo_RecordPath.Text = instruction.StandardSetRecordPath;
            this.cbo_KeyPath.Text = instruction.StandardSetKeyField;
            this.txt_ResultFilePath.Text = instruction.ResultFilePath;
        }

        private void PopulateTables(string connStr)
        {
            this.cbo_Tables.Items.Clear();
            string[] tblNames = DbUtil.GetTableNames(DataProviderType.MSSQL, string.Empty, connStr);
            if(tblNames != null && tblNames.Length>0)
            {
                foreach (string tblName in tblNames)
                    this.cbo_Tables.Items.Add(tblName);
            }
        }

        private void SelectComboBoxItem(ComboBox comboBox, string value)
        {
            if(comboBox.Items.Count>0 && !string.IsNullOrEmpty(value))
            {
                for(int i=0;i<comboBox.Items.Count;i++)
                {
                    if(comboBox.Items[i].ToString()==value)
                    {
                        comboBox.SelectedIndex = i;
                        break;
                    }
                }
            }
        }

        /// <summary></summary>
        public void SaveSettings()
        {
            SearchFieldContributionInstruction instruction =
                (SearchFieldContributionInstruction) this._FieldContributor.Instruction;
            instruction.ConnStr = this.txt_ConnStr.Text;
            instruction.TableName = this.cbo_Tables.Text;
            instruction.KeyField = this.cbo_KeyField.Text;
            instruction.TitleField = this.cbo_TitleField.Text;
            instruction.TextFields=new List<string>();
            foreach(DataGridViewRow dr in this.dv_TextFields.Rows)
            {
                if(!dr.IsNewRow && dr.Cells[0].Value !=null)
                    instruction.TextFields.Add((string)dr.Cells[0].Value);
            }
            instruction.Query = this.txt_Query.Text;
            instruction.FilterMethod = (FullTextSearchFilterMethod) this.cbo_FilterMethod.SelectedIndex;
            instruction.FilterThreshold = (double) this.num_FilterThreshold.Value;
            instruction.StandardDataSourceType = SetDataSourceType.ExcelFile;
            if(rd_Db.Checked)
                instruction.StandardDataSourceType = SetDataSourceType.DB;
            else if(rd_Xml.Checked)
                instruction.StandardDataSourceType = SetDataSourceType.XML;
            instruction.StandardSetDataSourcePath = this.txt_DataSourcePath.Text;
            instruction.StandardSetRecordPath = this.cbo_RecordPath.Text;
            instruction.StandardSetKeyField = this.cbo_KeyPath.Text;
            instruction.ResultFilePath = this.txt_ResultFilePath.Text;

            this._FieldContributor.Instruction = instruction;
        }

        /// <summary></summary>
        public TestResult Test()
        {
            throw new System.NotImplementedException();
        }

        #endregion

        private void cbo_Tables_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(this.cbo_Tables.SelectedIndex>=0)
            {
                string tblName = this.cbo_Tables.Text;
                string connStr = this.txt_ConnStr.Text;
                this.PopulateTableColumns(connStr,tblName);
            }
        }

        private void PopulateTableColumns(string connStr, string tblName)
        {
            this.cbo_KeyField.Items.Clear();
            this.cbo_TextField.Items.Clear();
            this.cbo_TitleField.Items.Clear();
            TableColumn[] cols = DbUtil.GetTableColumns(DataProviderType.MSSQL, connStr, string.Empty, tblName);
            if (cols != null && cols.Length > 0)
            {
                foreach (TableColumn col in cols)
                {
                    this.cbo_KeyField.Items.Add(col.ColumnName);
                    this.cbo_TitleField.Items.Add(col.ColumnName);
                    this.cbo_TextField.Items.Add(col.ColumnName);
                }
            }
        }

        private void rd_Db_CheckedChanged(object sender, EventArgs e)
        {
            SetDataSourceType srcType = SetDataSourceType.DB;
            if (this.rd_Db.Checked)
                srcType = SetDataSourceType.DB;
            else if (this.rd_Xml.Checked)
                srcType = SetDataSourceType.XML;
            else if (this.rd_Excel.Checked)
                srcType = SetDataSourceType.ExcelFile;
            this.UpdateDataSourceType(srcType);
        }

        private void rd_Xml_CheckedChanged(object sender, EventArgs e)
        {
            SetDataSourceType srcType = SetDataSourceType.DB;
            if (this.rd_Db.Checked)
                srcType = SetDataSourceType.DB;
            else if (this.rd_Xml.Checked)
                srcType = SetDataSourceType.XML;
            else if (this.rd_Excel.Checked)
                srcType = SetDataSourceType.ExcelFile;
            this.UpdateDataSourceType(srcType);
        }

        private void rd_Excel_CheckedChanged(object sender, EventArgs e)
        {
            SetDataSourceType srcType = SetDataSourceType.DB;
            if (this.rd_Db.Checked)
                srcType = SetDataSourceType.DB;
            else if (this.rd_Xml.Checked)
                srcType = SetDataSourceType.XML;
            else if (this.rd_Excel.Checked)
                srcType = SetDataSourceType.ExcelFile;
            this.UpdateDataSourceType(srcType);
        }

        private void UpdateDataSourceType(SetDataSourceType srcType)
        {
            if (srcType == SetDataSourceType.DB)
            {
                this.lbl_DataSourcePath.Text = "Connection:";
                this.lbl_RecordPath.Text = "Table:";
                this.btn_BrowseDataSource.Visible = false;
            }
            else if (srcType == SetDataSourceType.XML || srcType == SetDataSourceType.ExcelFile)
            {
                this.lbl_DataSourcePath.Text = "Source file:";
                this.lbl_RecordPath.Text = "Record path:";
                this.btn_BrowseDataSource.Visible = true;
            }
        }

        private void btn_BrowseDataSource_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            if (this.rd_Xml.Checked)
            {
                dlg.Filter = "Xml files(*.find;*.xml)|*.find;*.xml|All files(*.*)|*.*";
            }
            else if (this.rd_Excel.Checked)
            {
                dlg.Filter = "Excel files(*.xls;*.xlsx)|*.xls;*.xlsx|All files(*.*)|*.*";
            }
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.txt_DataSourcePath.Text = dlg.FileName;
            }
        }

        private void btn_RefreshRecordPaths_Click(object sender, EventArgs e)
        {
            if (this.rd_Db.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                this.cbo_RecordPath.Items.Clear();
                string[] tblNames = DbUtil.GetTableNames(DataProviderType.MSSQL, string.Empty, this.txt_DataSourcePath.Text);
                if (tblNames != null && tblNames.Length > 0)
                {
                    foreach (string tblName in tblNames)
                    {
                        this.cbo_RecordPath.Items.Add(tblName);
                    }
                }
            }
            else if (this.rd_Xml.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                XmlTextReader xReader = new XmlTextReader(File.OpenRead(this.txt_DataSourcePath.Text));
                this.cbo_RecordPath.Items.Clear();
                List<string> allPaths = new List<string>();
                int lastDepth = -1;
                string lastNodeName = string.Empty;
                Stack<string> elementNames = new Stack<string>();
                while (xReader.Read())
                {
                    if (xReader.NodeType == XmlNodeType.Element)
                    {
                        int currentDepth = xReader.Depth;
                        if (currentDepth > lastDepth)
                        {
                            lastDepth = currentDepth;
                            lastNodeName = xReader.Name;
                            elementNames.Push(lastNodeName);
                            string xPath = this.GetXPath(elementNames);
                            if (!allPaths.Contains(xPath))
                                allPaths.Add(xPath);
                        }
                        else if (currentDepth == lastDepth)
                        {
                            if (xReader.Name == lastNodeName)
                                continue;
                            else
                            {
                                lastNodeName = xReader.Name;
                                if (elementNames.Count > 0)
                                    elementNames.Pop();
                                elementNames.Push(lastNodeName);
                                string xPath = this.GetXPath(elementNames);
                                if (!allPaths.Contains(xPath))
                                    allPaths.Add(xPath);
                            }
                        }
                        else
                        {
                            lastDepth = xReader.Depth;
                            lastNodeName = xReader.Name;
                            while (elementNames.Count > xReader.Depth)
                                elementNames.Pop();
                            elementNames.Push(lastNodeName);
                            string xPath = this.GetXPath(elementNames);
                            if (!allPaths.Contains(xPath))
                                allPaths.Add(xPath);
                        }
                    }
                    else if (xReader.NodeType == XmlNodeType.EndElement)
                    {

                    }
                }
                xReader.Close();
                foreach (string xPath in allPaths)
                {
                    this.cbo_RecordPath.Items.Add(xPath);
                }
            }
            else if (this.rd_Excel.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                List<string> sheetNames = ExcelReader.GetWorksheetNames(this.txt_DataSourcePath.Text);
                this.cbo_RecordPath.Items.Clear();
                foreach (string sheetName in sheetNames)
                {
                    this.cbo_RecordPath.Items.Add(sheetName);
                }
            }
        }

        private string GetXPath(Stack<string> elementNames)
        {
            string[] elementNamesCopy = elementNames.ToArray();
            string path = string.Empty;
            for (int i = elementNamesCopy.Length - 1; i >= 0; i--)
            {
                if (path.Length > 0)
                    path += "/";
                path += elementNamesCopy[i];
            }
            return path;
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btn_OK_Click(object sender, EventArgs e)
        {
            this.SaveSettings();
            if (this.UpdateComponentSettingEvent != null)
                this.UpdateComponentSettingEvent(this._ActivityNode, this._FieldContributor);
            this.Close();
        }

        private void btn_SaveAs_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg=new SaveFileDialog();
            dlg.Filter = "File contributor files(*.xml)|*.xml|All files(*.*)|*.*";
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                this.txt_ResultFilePath.Text = dlg.FileName;
        }

        private void btn_Refresh2_Click(object sender, EventArgs e)
        {
            if(!string.IsNullOrEmpty(this.txt_ConnStr.Text))
                this.PopulateTables(this.txt_ConnStr.Text);
        }

        private void btn_AddField_Click(object sender, EventArgs e)
        {
            if(this.cbo_TextField.SelectedIndex>=0)
            {
                DataGridViewRow dr = new DataGridViewRow();
                DataGridViewTextBoxCell cellField=new DataGridViewTextBoxCell();
                cellField.Value = this.cbo_TextField.SelectedItem.ToString();
                dr.Cells.Add(cellField);
                this.dv_TextFields.Rows.Add(dr);
            }
        }

        private void cbo_RecordPath_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.cbo_RecordPath.SelectedIndex < 0)
                return;
            if (this.rd_Db.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                string tblName = this.cbo_RecordPath.SelectedItem.ToString();
                this.cbo_KeyPath.Items.Clear();
                TableColumn[] cols = DbUtil.GetTableColumns(DataProviderType.MSSQL, this.txt_DataSourcePath.Text, string.Empty,
                                                            tblName);
                if (cols != null && cols.Length > 0)
                {
                    foreach (TableColumn col in cols)
                    {
                        this.cbo_KeyPath.Items.Add(col.ColumnName);
                    }
                }
            }
            else if (this.rd_Xml.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                this.cbo_KeyPath.Items.Clear();
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(this.txt_DataSourcePath.Text);
                XmlNodeList xNodes = xDoc.SelectNodes(this.cbo_RecordPath.SelectedItem.ToString());
                if (xNodes != null && xNodes.Count > 0)
                {
                    XmlNode xNode = xNodes[0];
                    foreach (XmlAttribute attr in xNode.Attributes)
                    {
                        this.cbo_KeyPath.Items.Add(attr.Name);
                    }
                }
            }
            else if (this.rd_Excel.Checked && !string.IsNullOrEmpty(this.txt_DataSourcePath.Text))
            {
                this.cbo_KeyPath.Items.Clear();
                string sheetName = this.cbo_RecordPath.SelectedItem.ToString();
                List<string> colHeaders = ExcelReader.ReadColumnHeaders(this.txt_DataSourcePath.Text, sheetName);
                if (colHeaders != null && colHeaders.Count > 0)
                {
                    foreach (string colName in colHeaders)
                    {
                        this.cbo_KeyPath.Items.Add(colName);
                    }
                }
            }
        }
    }
}
